﻿/******************************************************************************
* SunnyUI.FrameDecoder 开源TCP、串口数据解码库。
* CopyRight (C) 2022-2023 ShenYongHua(沈永华).
* QQ群：56829229 QQ：17612584 EMail：SunnyUI@qq.com
*
* Blog:   https://www.cnblogs.com/yhuse
* Gitee:  https://gitee.com/yhuse/SunnyUI.FrameDecoder
*
* SunnyUI.FrameDecoder.dll can be used for free under the MIT license.
* If you use this code, please keep this note.
* 如果您使用此代码，请保留此说明。
******************************************************************************
* 文件名称: BaseStringFrameDecoder.cs
* 文件说明: 字符串帧解码器基类
* 当前版本: V1.0
* 创建日期: 2022-11-01
*
* 2022-11-01: V1.0.0 增加文件说明
******************************************************************************/

using System;
using System.Text;

namespace Sunny.FrameDecoder
{
    /// <summary>
    /// 字符串帧解码器基类
    /// </summary>
    public abstract class BaseStringFrameDecoder : BaseFrameDecoder
    {
        /// <summary>
        /// 数据缓存
        /// </summary>
        protected readonly DataCache<char> Cache = new();

        /// <summary>
        /// 数据类型
        /// </summary>
        public sealed override FrameDataType DataType => FrameDataType.ASCII;

        /// <summary>
        /// 编码
        /// </summary>
        public Encoding Encoding { get; set; } = Encoding.ASCII;

        /// <summary>
        /// 释放对象
        /// </summary>
        /// <param name="disposing">ture时一般位主动调用释放，false一般为GC调用析构函数时释放</param>
        protected override void Dispose(bool disposing)
        {
            if (!IsDisposed && disposing)
            {
                IsDisposed = true;

                //释放资源
                Cache.Dispose();
            }
        }

        /// <summary>
        /// 解码
        /// </summary>
        /// <param name="data">输入数据</param>
        public void Decode(string data)
        {
            CheckTimeout();

            if (string.IsNullOrEmpty(data)) return;
            if (!PrepareDecode(ref data)) return;
            if (Cache.WrittenCount + data.Length >= CacheMaxSize) Reset();
            Cache.Add(data.AsSpan());
            if (Cache.WrittenCount < FrameExceptDataLength) return;
            Decoding();
            FinishDecode();
            DoAfterDecoder(this);
        }

        /// <summary>
        /// 解码
        /// </summary>
        /// <param name="data">输入数据</param>
        public void Decode(ReadOnlySpan<byte> data)
        {
#if NET6_0_OR_GREATER
            Decode(Encoding.GetString(data));
#else
            Decode(Encoding.GetString(data.ToArray()));
#endif
        }

        /// <summary>
        /// 准备数据解码
        /// </summary>
        /// <param name="data">输入数据</param>
        /// <returns>判断此数据是否可以解码</returns>
        protected virtual bool PrepareDecode(ref string data)
        {
            if (string.IsNullOrEmpty(data))
            {
                InvokeDecoderError("The data to be decoded is null.", data);
                return false;
            }

            return true;
        }

        /// <summary>
        /// 缓存复位
        /// </summary>
        public override void Reset()
        {
            Cache.Clear();
        }

        /// <summary>
        /// 解码输出事件
        /// </summary>
        public event EventHandler<StringFrameEventArgs> OnDecoder;

        /// <summary>
        /// 调用解码输出事件
        /// </summary>
        /// <param name="e">数据</param>
        protected void InvokeOnDecoder(StringFrameEventArgs e) => OnDecoder?.Invoke(this, e);

        /// <summary>
        /// 解码错误事件
        /// </summary>
        public event EventHandler<StringDecoderExceptionEventArgs> OnDecoderError;

        internal void InvokeDecoderError(string message, string value) =>
            OnDecoderError?.Invoke(this, new StringDecoderExceptionEventArgs(message, value));
    }
}
